#pragma once
#include "renderer.h"
#include "matrix.h"
#include "image_file.h"
#include "render_job.h"
#include <vector>
#include <string>

namespace krryn{
namespace math{ class matrix; }
namespace core{ class job_manager; }
namespace system{ class input_manager; }
namespace view{ class camera; }
namespace system{
	template<>
	class renderer<software_tag>{
	public:
		typedef std::auto_ptr<renderer<software_tag> > ptr;
		typedef software_tag render_tag;
		typedef system_imp::internalSoftwareRasterizerType data_type;

		renderer(core::job_manager *a_Manager, view::canvas *a_Canvas) : m_Activate(0), m_Canvas(a_Canvas){
			m_ProjectionMatrix = matrix::create_perspective_projection(1.f, 1000.f, 1, .75);
			std::cout << "Using " << typeid(data_type).name() << " pipeline" << std::endl;
		}

		template<typename _RenderTag>
		renderer(core::job_manager *a_Manager, gui::render_control<_RenderTag> &a_Control)
			: m_Activate(0){
			attach_to(a_Control);
			m_ProjectionMatrix = matrix::create_perspective_projection(1.f, 1000.f, 1, .75);

			std::cout << "Using " << typeid(data_type).name() << " pipeline" << std::endl;
		};

		void set_canvas(view::canvas *a_Canvas){
			m_Canvas = a_Canvas;
			m_Activate = 0;
		}

		void attach_to(gui::render_control<software_tag> &a_Control){
			if(m_Activate != 0) delete m_Activate;

			m_Activate = new activate_t<software_tag>(*this, a_Control);
		}

		void begin_scene(){
			if(m_Activate){
				m_Canvas = m_Activate->m_Control.lock();
			}else{
				m_Canvas->lock();
			}
			m_Canvas->clear(0);
			m_TriangleRenderer.set_canvas(m_Canvas);
		}

		data_type *get_depth_buffer(){ return m_TriangleRenderer.get_depth_buffer_ptr(); }

		template<typename T>
		void draw(T &a_Mesh, const math::matrix &a_Transformation){
			// not equipped to deal with untextured meshes
			if(a_Mesh.effects().empty() || !a_Mesh.effects().front()) return;

			matrix l_Temp = m_ProjectionMatrix;
			l_Temp.concatenate(a_Transformation);

			system_imp::render_job_data<scene::vertex, data_type> l_Data(
				a_Mesh.get_vertex_buffer(), 
				a_Mesh.get_index_buffer(), 
				a_Mesh.get_aabb(),
				m_TriangleRenderer
			);

			l_Data.m_WorldToClip = l_Temp;
			l_Data.m_Canvas = get_canvas();
			l_Data.m_Texture = (system_imp::texture_software*)a_Mesh.effects().front();

			system_imp::render_job<scene::vertex, data_type> l_Job(l_Data);
			l_Job.execute();
		}

		view::canvas *get_canvas(){
			return m_Canvas;
		}

		void end_scene(){
			if(m_Activate){
				m_Activate->m_Control.unlock();
				m_Activate->m_Control.swap_buffers();
				m_Canvas = 0;
			}else{
				m_Canvas->unlock();
			}
		}

		void set_projection_matrix(const math::matrix &a_ProjectionMatrix){
			m_ProjectionMatrix = a_ProjectionMatrix;
		}

		void set_camera(const view::camera &a_Camera){
		}
	private:
		typedef system::activate_t<system::software_tag>* activate_ptr;

		activate_ptr m_Activate;

		view::canvas *m_Canvas;
		math::matrix m_ProjectionMatrix;
		system_imp::triangle_renderer_software<scene::vertex, data_type> m_TriangleRenderer;
    };
}
}
